using System;
using System.Reflection;
using System.Web.Mvc;
using Dyn.News.Extensions;
using Dyn.News.Models;
using Dyn.News.Services;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Aspects;
using Orchard.Core.Contents.Settings;
using Orchard.Localization;
using Orchard.Mvc.AntiForgery;
using Orchard.Mvc.Extensions;
using Orchard.UI.Admin;
using Orchard.UI.Notify;
using Orchard;

namespace Dyn.News.Controllers {
    [ValidateInput(false), Admin]
    public class ArticleAdminController : Controller, IUpdateModel {
        private readonly ICategoryervice _Categoryervice;
        private readonly IArticleService _ArticleService;

        public ArticleAdminController(IOrchardServices services, ICategoryervice Categoryervice, IArticleService ArticleService) {
            Services = services;
            _Categoryervice = Categoryervice;
            _ArticleService = ArticleService;
            T = NullLocalizer.Instance;
        }

        public IOrchardServices Services { get; set; }
        public Localizer T { get; set; }

        public ActionResult Create(int categoryID) {

            var Category = _Categoryervice.Get(categoryID, VersionOptions.Latest).As<NewsCategoryPart>();
            if (Category == null)
                return HttpNotFound();

            var Article = Services.ContentManager.New<ArticlePart>("Article");
            if(Article==null)
                return HttpNotFound();
            Article.CategoryPart = Category;
           
            if (!Services.Authorizer.Authorize(Permissions.EditArticle, Article, T("Not allowed to create category post")))
                return new HttpUnauthorizedResult();

            dynamic model = Services.ContentManager.BuildEditor(Article);
            
            // Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation.
            return View((object)model);
        }

        [HttpPost, ActionName("Create")]
        [FormValueRequired("submit.Save")]
        public ActionResult CreatePOST(int categoryID) {
            return CreatePOST(categoryID, false);
        }

        [HttpPost, ActionName("Create")]
        [FormValueRequired("submit.Publish")]
        public ActionResult CreateAndPublishPOST(int categoryID) {
            if (!Services.Authorizer.Authorize(Permissions.PublishOwnArticle, T("Couldn't create content")))
                return new HttpUnauthorizedResult();

            return CreatePOST(categoryID, true);
        }

        private ActionResult CreatePOST(int categoryID, bool publish = false) {
            var Category = _Categoryervice.Get(categoryID, VersionOptions.Latest).As<NewsCategoryPart>();

            if (Category == null)
                return HttpNotFound();

            var Article = Services.ContentManager.New<ArticlePart>("Article");
            Article.CategoryPart = Category;

            if (!Services.Authorizer.Authorize(Permissions.EditArticle, Article, T("Couldn't create category post")))
                return new HttpUnauthorizedResult();
            
            Services.ContentManager.Create(Article, VersionOptions.Draft);
            var model = Services.ContentManager.UpdateEditor(Article, this);

            if (!ModelState.IsValid) {
                Services.TransactionManager.Cancel();
                // Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation.
                return View((object)model);
            }

            if (publish) {
                if (!Services.Authorizer.Authorize(Permissions.PublishArticle, Category.ContentItem, T("Couldn't publish category post")))
                    return new HttpUnauthorizedResult();

                Services.ContentManager.Publish(Article.ContentItem);
            }

            Services.Notifier.Information(T("Your {0} has been created.", Article.TypeDefinition.DisplayName));
            return Redirect(Url.ArticleEdit(Article));
        }

        //todo: the content shape template has extra bits that the core contents module does not (remove draft functionality)
        //todo: - move this extra functionality there or somewhere else that's appropriate?
        public ActionResult Edit(int categoryID, int articleId) {
            var Category = _Categoryervice.Get(categoryID, VersionOptions.Latest);
            if (Category == null)
                return HttpNotFound();

            var post = _ArticleService.Get(articleId, VersionOptions.Latest);
            if (post == null)
                return HttpNotFound();

            if (!Services.Authorizer.Authorize(Permissions.EditArticle, post, T("Couldn't edit category post")))
                return new HttpUnauthorizedResult();

            dynamic model = Services.ContentManager.BuildEditor(post);
            // Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation.
            return View((object)model);
        }

        [HttpPost, ActionName("Edit")]
        [FormValueRequired("submit.Save")]
        public ActionResult EditPOST(int categoryID, int articleId, string returnUrl) {
            return EditPOST(categoryID, articleId, returnUrl, contentItem => {
                if (!contentItem.Has<IPublishingControlAspect>() && !contentItem.TypeDefinition.Settings.GetModel<ContentTypeSettings>().Draftable)
                    Services.ContentManager.Publish(contentItem);
            });
        }

        [HttpPost, ActionName("Edit")]
        [FormValueRequired("submit.Publish")]
        public ActionResult EditAndPublishPOST(int categoryID, int articleId, string returnUrl) {
            var Category = _Categoryervice.Get(categoryID, VersionOptions.Latest);
            if (Category == null)
                return HttpNotFound();

            // Get draft (create a new version if needed)
            var Article = _ArticleService.Get(articleId, VersionOptions.DraftRequired);
            if (Article == null)
                return HttpNotFound();

            if (!Services.Authorizer.Authorize(Permissions.PublishArticle, Article, T("Couldn't publish category post")))
                return new HttpUnauthorizedResult();

            return EditPOST(categoryID, articleId, returnUrl, contentItem => Services.ContentManager.Publish(contentItem));
        }

        public ActionResult EditPOST(int categoryID, int articleId, string returnUrl, Action<ContentItem> conditionallyPublish) {
            var Category = _Categoryervice.Get(categoryID, VersionOptions.Latest);
            if (Category == null)
                return HttpNotFound();

            // Get draft (create a new version if needed)
            var Article = _ArticleService.Get(articleId, VersionOptions.DraftRequired);
            if (Article == null)
                return HttpNotFound();

            if (!Services.Authorizer.Authorize(Permissions.EditArticle, Article, T("Couldn't edit category post")))
                return new HttpUnauthorizedResult();

            // Validate form input
            var model = Services.ContentManager.UpdateEditor(Article, this);
            if (!ModelState.IsValid) {
                Services.TransactionManager.Cancel();
                // Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation.
                return View((object)model);
            }

            conditionallyPublish(Article.ContentItem);

            Services.Notifier.Information(T("Your {0} has been saved.", Article.TypeDefinition.DisplayName));

            return this.RedirectLocal(returnUrl, Url.ArticleEdit(Article));
        }

        [ValidateAntiForgeryTokenOrchard]
        public ActionResult DiscardDraft(int id) {
            // get the current draft version
            var draft = Services.ContentManager.Get(id, VersionOptions.Draft);
            if (draft == null) {
                Services.Notifier.Information(T("There is no draft to discard."));
                return RedirectToEdit(id);
            }

            // check edit permission
            if (!Services.Authorizer.Authorize(Permissions.EditArticle, draft, T("Couldn't discard category post draft")))
                return new HttpUnauthorizedResult();

            // locate the published revision to revert onto
            var published = Services.ContentManager.Get(id, VersionOptions.Published);
            if (published == null) {
                Services.Notifier.Information(T("Can not discard draft on unpublished category post."));
                return RedirectToEdit(draft);
            }

            // marking the previously published version as the latest
            // has the effect of discarding the draft but keeping the history
            draft.VersionRecord.Latest = false;
            published.VersionRecord.Latest = true;

            Services.Notifier.Information(T("Category post draft version discarded"));
            return RedirectToEdit(published);
        }

        ActionResult RedirectToEdit(int id) {
            return RedirectToEdit(Services.ContentManager.GetLatest<ArticlePart>(id));
        }

        ActionResult RedirectToEdit(IContent item) {
            if (item == null || item.As<ArticlePart>() == null)
                return HttpNotFound();
            return RedirectToAction("Edit", new { categoryID = item.As<ArticlePart>().CategoryPart.Id, articleId = item.ContentItem.Id });
        }

        [ValidateAntiForgeryTokenOrchard]
        public ActionResult Delete(int categoryID, int articleId) {
            //refactoring: test PublishArticle/PublishArticle in addition if published

            var Category = _Categoryervice.Get(categoryID, VersionOptions.Latest);
            if (Category == null)
                return HttpNotFound();

            var post = _ArticleService.Get(articleId, VersionOptions.Latest);
            if (post == null)
                return HttpNotFound();

            if (!Services.Authorizer.Authorize(Permissions.DeleteArticle, post, T("Couldn't delete category post")))
                return new HttpUnauthorizedResult();

            _ArticleService.Delete(post);
            Services.Notifier.Information(T("Category post was successfully deleted"));

            return Redirect(Url.CategoryForAdmin(Category.As<NewsCategoryPart>()));
        }

        [ValidateAntiForgeryTokenOrchard]
        public ActionResult Publish(int categoryID, int articleId) {
            var Category = _Categoryervice.Get(categoryID, VersionOptions.Latest);
            if (Category == null)
                return HttpNotFound();

            var post = _ArticleService.Get(articleId, VersionOptions.Latest);
            if (post == null)
                return HttpNotFound();

            if (!Services.Authorizer.Authorize(Permissions.PublishArticle, post, T("Couldn't publish category post")))
                return new HttpUnauthorizedResult();

            _ArticleService.Publish(post);
            Services.Notifier.Information(T("Category post successfully published."));

            return Redirect(Url.CategoryForAdmin(Category.As<NewsCategoryPart>()));
        }

        [ValidateAntiForgeryTokenOrchard]
        public ActionResult Unpublish(int categoryID, int articleId) {
            var Category = _Categoryervice.Get(categoryID, VersionOptions.Latest);
            if (Category == null)
                return HttpNotFound();

            var post = _ArticleService.Get(articleId, VersionOptions.Latest);
            if (post == null)
                return HttpNotFound();

            if (!Services.Authorizer.Authorize(Permissions.PublishArticle, post, T("Couldn't unpublish category post")))
                return new HttpUnauthorizedResult();

            _ArticleService.Unpublish(post);
            Services.Notifier.Information(T("Category post successfully unpublished."));

            return Redirect(Url.CategoryForAdmin(Category.As<NewsCategoryPart>()));
        }

        bool IUpdateModel.TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) {
            return TryUpdateModel(model, prefix, includeProperties, excludeProperties);
        }

        void IUpdateModel.AddModelError(string key, LocalizedString errorMessage) {
            ModelState.AddModelError(key, errorMessage.ToString());
        }
    }

    public class FormValueRequiredAttribute : ActionMethodSelectorAttribute {
        private readonly string _submitButtonName;

        public FormValueRequiredAttribute(string submitButtonName) {
            _submitButtonName = submitButtonName;
        }

        public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) {
            var value = controllerContext.HttpContext.Request.Form[_submitButtonName];
            return !string.IsNullOrEmpty(value);
        }
    }
}